#!/usr/bin/env ruby
require 'rexml/document'
require 'yaml'
require 'erb'
load File.expand_path(File.dirname(__FILE__) + '/interface-template.erb')

# Signature grammar
require 'rubygems'
require 'kanocc'

# --- Define grammar for typesignatures ---------
class SimpleType < Kanocc::Token
  attr_reader :type
  pattern(/u|i|s|v/) do # FIXME all simple types here. 
    case @m[0]
      when "u" : @type = "unsigned int"
      when "i" : @type = "int"
      when "s" : @type = "std::string"
      when "v" : @type = "Variant"
    end
  end
end

class SingleCompleteType < Kanocc::Nonterminal
  attr_reader :type
  rule(SimpleType) {@type = @rhs[0].type}
  rule("a", SingleCompleteType) {@type = "std::vector<#{@rhs[1].type}>"}
  rule("a", "{", SingleCompleteType, SingleCompleteType, "}") {@type = "std::map<#{@rhs[2].type}, #{@rhs[3].type}>"}
end
# ---------- End grammar ------------------------

class Interface
  attr_accessor :xml, :name, :interface, :methods, :signals, :header_tag
end

class InterfaceMethod 
  attr_accessor :name, :parameters
  
  def in_parameters
    @parameters.find_all{|p| p.direction == :in}
  end
   
  def out_parameters
    @parameters.find_all{|p| p.direction == :out}
  end

  def parameter_list
    @parameters.map{|p| p.name}.join(", ")
  end
end

class InterfaceSignal
	attr_accessor :name, :parameters
	
	def parameter_list
		@parameters.map{|p| p.name}.join(", ")
	end
end

class MethodParameter
  attr_accessor :name, :type, :signature, :direction
end

class SignalParameter
	attr_accessor :name, :type, :signature
end

class DBusGenerator
  def initialize
    parse_options
    klass = parse_xml
    File.open(klass.name + ".h", "w+") do |f|
      f.write(ERB.new(Template, nil, "%<>").result(binding))
    end
  end

  def parse_options 
    require 'optparse'
    namespace = ""
    opts = OptionParser.new
    opts.on("-n", 
            "--namespace=NAMESPACE", 
            String, 
            "Namespace for the interface class") {|v| namespace = v}
    remains = opts.parse(ARGV)
    if remains.size > 1 
      puts opts.to_s
      exit(1)
    elsif remains.size == 1
      @io = File.open(remains[0])
    else
      @io = $stdin
    end 
    @namespace = namespace
  end
 
  def parse_xml
    doc =  REXML::Document.new(@io).root
    interface = Interface.new
    interface.xml = doc.root.to_s 
    interface.interface = doc.root.attributes['name'] 
    interface.name = doc.root.attributes['name'].gsub(/\./, "_") 
    interface.header_tag = interface.name.upcase + "_H_" 
    interface.methods = doc.elements.find_all {|el| el.name = 'method'}.map{|m| method(m)}
    interface.signals = doc.elements.find_all {|el| el.name = 'signal'}.map{|s| signal(s)} 
		return interface
  end

  def method(element)
    m = InterfaceMethod.new
    m.name = element.attributes['name']
    m.parameters = element.elements.find_all {|e| e.name = 'arg'}.map {|e| parameter(e)}
    return m 
  end

  def parameter(element)
    param =  MethodParameter.new
    param.name = "v_" + element.attributes['name']
    param.signature = element.attributes['type']
    param.direction = (element.attributes['direction'] == 'out' ? :out : :in)
    param.type = type(param.signature)
    return param 
  end
 
  def signal(element)
		s = InterfaceSignal.new
		s.name = element.attributes['name']
		s.parameters = element.elements.find_all {|e| e.name = 'arg'}.map {|e| signal_parameter(e)}
  end

	def signal_parameter(element)
		param = SignalParameter.new
		param.name = "s_" + element.attributes['name']
		param.signature = element.attributes['type']
		param.type = type(param.signature)
    return param	
	end

  TypeMap = {"u" => "unsigned int",
             "i" => "int",
             "s" => "std::string",
             "v" => "Variant"};
  TypeParser = Kanocc::Kanocc.new(SingleCompleteType)
  
  def type(sig) 
    c_type = TypeParser.parse(sig).type   
    return c_type 
  end

  def methodCall(m)
    methodName = m.attributes['name'] 
    typeinfo = get_typeinfo(m) 
    
    <<-EOS.pp(binding)
   EOS
  end

  

end

DBusGenerator.new
 
